﻿using System;

namespace LOHDemo
{
	class Program : IHost
	{
		/// <summary>
		/// Entry point
		/// </summary>
		/// <param name="args"></param>
		static void Main(string[] args)
		{
			Program p = new Program();
			p.Run();
		}

		/// <summary>
		/// Run the four tests.
		/// Test 1 : Allocating interlaced LOH objects
		///		Allocating interlaced objects on LOH and freeing one set of interlaced objects
		///		will result in lots of free memory but a severely fragmented heap which will
		///		prevent further large objects being allocated.
		/// Test 2 : Allocating non interlaced LOH objects
		///		As test 1 but without the interlacing of the allocations on the LOH. In this case
		///		it will be able to continue to allocate large objects.
		///	Test 3 : Allocating interlaced LOH objects with release
		///		As test 1, but without interlacing the allocations. This will mean that the LOH
		///		has larger holes and will allow large objects to continue to allocate.
		///	Test 4 : Proving compaction in the SOH
		///		Exactly the same as test 1, but allocating smaller objects on the SOH. When
		///		one of the interlaced lists is released this will continue to allow objects
		///		larger than what would be the largest hole to be allocated due to the SOH
		///		compacting memory after a GC.
		/// </summary>
		public void Run()
		{
			ITest[] tests = new ITest[]
			{
				new TestInterlacedMemoryAllocationResultsInOutOfMemory(),
				new TestNonInterlacedMemoryAllocationDoesNotResultInOutOfMemory(),
				new TestInterlacedMemoryAllocationWithFullReleaseDoesNotResultInOutOfMemory(),
				new TestSmallObjectHeapInterlacedAllocationDoesCompaction()
			};

			foreach (ITest test in tests)
				test.Execute(this);
		}

		/// <summary>
		/// Called by tests to output a message + the current memory usage.
		/// </summary>
		/// <param name="log"></param>
		public void MemoryUsed(string log)
		{
			long used = GC.GetTotalMemory(false);
			ColorText(ConsoleColor.Gray, "{0, -100}\t (using: {1})", log, used);
		}

		/// <summary>
		/// Called by tests to output an error notice
		/// </summary>
		/// <param name="log"></param>
		public void Error(string log)
		{
			ColorText(ConsoleColor.Red, log);
		}

		/// <summary>
		/// Called by tests to output the start of test message
		/// </summary>
		/// <param name="log"></param>
		public void StartTest(string log)
		{
			ColorText(ConsoleColor.Yellow, String.Format("\n\r\n\r{0}", log));
		}

		/// <summary>
		/// Utility to output text to the console in a specified colour.
		/// </summary>
		/// <param name="color"></param>
		/// <param name="format"></param>
		/// <param name="args"></param>
		private void ColorText(ConsoleColor color, string format, params object [] args )
		{
			ConsoleColor original = Console.ForegroundColor;
			Console.ForegroundColor = color;
			Console.WriteLine(format, args);
			Console.ForegroundColor = original;
		}
	}
}
